package junit.source.pattern.chain;


import junit.source.pattern.listener.RunNotifier;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.runner.notification.Failure;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.TestClass;

import java.util.List;

public abstract class ParentRunner {

    private final TestClass testClass;

    protected ParentRunner(Class<?> testClass) throws InitializationError {
        this.testClass = new TestClass(testClass);
    }

    public void run(RunNotifier notifier) {
        try {
            Statement statement = classBlock(notifier);
            statement.evaluate();
        } catch (Throwable e) {
            notifier.fireTestFailure(new Failure(null, e));
        }
    }

    /**
     * 这个方法描述了类级别的测试用例，beforeClasses -> testCases -> afterClasses
     *
     * @param notifier
     * @return
     */
    protected Statement classBlock(RunNotifier notifier) {
        Statement statement = childrenInvoker(notifier);
        statement = withBeforeClasses(statement);
        statement = withAfterClasses(statement);
        return statement;
    }

    protected Statement childrenInvoker(RunNotifier notifier) {
        return new Statement() {

            @Override
            public void evaluate() throws Throwable {
                runChildren(notifier);
            }
        };
    }

    private Statement withBeforeClasses(Statement statement) {
        List<FrameworkMethod> beforeMethods = testClass.getAnnotatedMethods(BeforeClass.class);
        return beforeMethods.isEmpty() ? statement : new RunBefores(statement, beforeMethods, null);
    }

    private Statement withAfterClasses(Statement statement) {
        List<FrameworkMethod> afterMethods = testClass.getAnnotatedMethods(AfterClass.class);
        return afterMethods.isEmpty() ? statement : new RunAfters(statement, afterMethods, null);
    }

    private void runChildren(RunNotifier notifier) {
        for (final FrameworkMethod each : getFilteredChildren()) {
            runChild(each, notifier);
        }
    }

    /**
     * 不同的子类可以实现不同的testCase处理方式
     *
     * <li>BlockJUnit4ClassRunner </li>的封装 ：
     *  statement = methodBlock(method);
     *  try{
     *      statement.evaluate()
     *  }catch(){
     *
     *  }finally(){
     *
     *  }
     *
     *  来处理listener 和对应的测试用例
     *
     *  <li>Suit</li> 的封装了 多个测试类，即 持有 List<Runner> runners
     *  runChild的实现是 runner.run(notifier)
     *
     *
     * @param child
     * @param notifier
     */
    protected abstract void runChild(FrameworkMethod child, RunNotifier notifier);

    protected abstract List<FrameworkMethod> getFilteredChildren();
}
